﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using System.Xml.Linq;
using Caliburn.Micro;
using ConditionTreeTestStand.Nodes;
using RatingAuthor;
using SamizdatSearch.Properties;

namespace SamizdatSearch
{
  public class SearchResultsViewModel : PropertyChangedBase
  {
    private readonly ShellViewModel _owner;
    private readonly SiDatabase _db;
    private readonly byte[] _passwordHash = new byte[] { 254, 130, 11, 62, 250, 203, 6, 231, 73, 96, 19, 189, 245, 212, 171, 31, 136, 88, 81, 204 };
    private readonly IWindowManager _windowManager;

    #region ' SearchResults Property (change notification) '

    public IEnumerable<BookViewModel> SearchResults
    {
      get { return _searchResults; }
      set
      {
        if (_searchResults == value) return;
        _searchResults = value;
        NotifyOfPropertyChange(() => SearchResults);
      }
    }

    private IEnumerable<BookViewModel> _searchResults;

    #endregion

    #region ' MinMarksCount Prop '

    public int MinMarksCount
    {
      get { return _minMarksCount; }
      set
      {
        if (_minMarksCount == value) return;
        _minMarksCount = value;
        NotifyOfPropertyChange(() => MinMarksCount);
      }
    }

    private int _minMarksCount = 100;

    #endregion

    #region ' MaxMarksCount Property (change notification) '

    public int MaxMarksCount
    {
      get { return _maxMarksCount; }
      set
      {
        if (_maxMarksCount == value) return;
        _maxMarksCount = value;
        NotifyOfPropertyChange(() => MaxMarksCount);
      }
    }

    private int _maxMarksCount = 999999;

    #endregion

    #region ' MinCommentsCount Property (change notification) '

    public int MinCommentsCount
    {
      get { return _minCommentsCount; }
      set
      {
        if (_minCommentsCount == value) return;
        _minCommentsCount = value;
        NotifyOfPropertyChange(() => MinCommentsCount);
      }
    }

    private int _minCommentsCount;

    #endregion

    #region ' MaxCommentsCount Property (change notification) '

    public int MaxCommentsCount
    {
      get { return _maxCommentsCount; }
      set
      {
        if (_maxCommentsCount == value) return;
        _maxCommentsCount = value;
        NotifyOfPropertyChange(() => MaxCommentsCount);
      }
    }

    private int _maxCommentsCount = 999999;

    #endregion

    #region ' MinSize Property (change notification) '

    public int MinSize
    {
      get { return _minSize; }
      set
      {
        if (_minSize == value) return;
        _minSize = value;
        NotifyOfPropertyChange(() => MinSize);
      }
    }

    private int _minSize = 500;

    #endregion

    #region ' MaxSize Property (change notification) '

    public int MaxSize
    {
      get { return _maxSize; }
      set
      {
        if (_maxSize == value) return;
        _maxSize = value;
        NotifyOfPropertyChange(() => MaxSize);
      }
    }

    private int _maxSize = 999999;

    #endregion

    #region ' AuthorNameLike Property (change notification) '

    public string AuthorNameLike
    {
      get { return _authorNameLike; }
      set
      {
        if (_authorNameLike == value) return;
        _authorNameLike = value;
        NotifyOfPropertyChange(() => AuthorNameLike);
      }
    }

    private string _authorNameLike = "";

    #endregion

    #region ' BookNameLike Property (change notification) '

    public string BookNameLike
    {
      get { return _bookNameLike; }
      set
      {
        if (_bookNameLike == value) return;
        _bookNameLike = value;
        NotifyOfPropertyChange(() => BookNameLike);
      }
    }

    private string _bookNameLike = "";

    #endregion

    #region ' GenresSelection Property (change notification) '

    public IEnumerable<GenreSelecor> GenresSelection
    {
      get { return _genresSelection; }
      set
      {
        if (_genresSelection == value) return;
        _genresSelection = value;
        NotifyOfPropertyChange(() => GenresSelection);
      }
    }

    private IEnumerable<GenreSelecor> _genresSelection;

    #endregion

    #region ' RealResultsCount Property (change notification) '

    public int RealResultsCount
    {
      get { return _realResultsCount; }
      set
      {
        if (_realResultsCount == value) return;
        _realResultsCount = value;
        NotifyOfPropertyChange(() => RealResultsCount);
      }
    }

    private int _realResultsCount;

    #endregion

    #region ' IsTooManyResults Property (change notification) '

    public bool IsTooManyResults
    {
      get { return _isTooManyResults; }
      set
      {
        if (_isTooManyResults == value) return;
        _isTooManyResults = value;
        NotifyOfPropertyChange(() => IsTooManyResults);
      }
    }

    private bool _isTooManyResults;

    #endregion

    public void UpdateDb()
    {
      var passwordViewModel = new PasswordViewModel();
      _windowManager.ShowDialog(passwordViewModel);
      if (!passwordViewModel.IsAccepted) return;
      if (string.IsNullOrEmpty(passwordViewModel.Password)) return;
      var p = Encoding.Default.GetBytes(passwordViewModel.Password);
      var hash = HashAlgorithm.Create("SHA1").ComputeHash(p);
      if (Arrays.AreEqual(hash, _passwordHash))
        _owner.UpdateDb(_db);
    }

    public SearchResultsViewModel(ShellViewModel owner, SiDatabase db, IWindowManager windowManager)
    {
      _owner = owner;
      _db = db;
      _windowManager = windowManager;

      GenresSelection = db.GenresLookup.Values.Select(g => new GenreSelecor(g, () => { })).ToList();

      InitConditionTree();

      // GenresSelection may be empty
      var fantacy = GenresSelection.FirstOrDefault(g => g.Name == "Фэнтези");
      if (fantacy != null) fantacy.IsSelected = true;

//      SetSearchResults(SimpleFilter);
    }

    public IEnumerable<Book> SimpleFilter(IEnumerable<Book> res)
    {
      if (MinMarksCount > 0) res = res.Where(b => b.MarksCount >= MinMarksCount);
      if (MaxMarksCount >= MinMarksCount) res = res.Where(b => b.MarksCount <= MaxMarksCount);
      if (MinCommentsCount > 0) res = res.Where(b => b.CommentsCount >= MinCommentsCount);
      if (MaxCommentsCount >= MinCommentsCount) res = res.Where(b => b.CommentsCount <= MaxCommentsCount);
      if (MinSize > 0) res = res.Where(b => b.Size >= MinSize);
      if (MaxSize >= MinSize) res = res.Where(b => b.Size <= MaxSize);
      if (BookNameLike != "") res = res.Where(b => b.Title.Contains(BookNameLike));
      if (AuthorNameLike != "") res = res.Where(b => b.Author.Name.Contains(AuthorNameLike));

      var ganres = _db.GenresLookup.Lookup(from g in GenresSelection where g.IsSelected select g.Name);
      if (ganres != 0) res = res.Where(b => (b.Ganres & ganres) != 0);
      return res;
    }

    public void SimpleSearch()
    {
      SetSearchResults(SimpleFilter);
    }
    public void SetSearchResults(Func<IEnumerable<Book>, IEnumerable<Book>> where)
    {
      var res = _db.AuthorsByUrl.Values.SelectMany(a => a.BooksByUrl.Values);
      var list = where(res).ToList();
      IsTooManyResults = list.Count > 1000;
      RealResultsCount = list.Count;
      SearchResults = list.Take(1000).
        Select(b => new BookViewModel(b, _db.GenresLookup)).
        ToList();

    }
    public void SaveDb()
    {
      _db.Save(p => { });
    }

    public ObservableCollection<NodeViewModel> Roots { get; private set; }

    public void InitConditionTree()
    {
      var nodeContext = new NodeContext(_db.GenresLookup, _windowManager);
      var rootNode = XmlSerializer.Deserialize(Settings.Default.Condition, nodeContext);
      Roots = rootNode.Children;
      rootNode.ConditionChanged += OnConditionChanged;
      OnConditionChanged(rootNode, EventArgs.Empty);
    }

    private readonly Deferrer _deferrer = new Deferrer(TimeSpan.FromSeconds(.1));
    private void OnConditionChanged(object sender, EventArgs eventArgs)
    {
      var tree = (RootNodeViewModel) sender;
      Settings.Default.Condition = XmlSerializer.Serialize(tree);
      _deferrer.Defer(() => SearchByTreeCondition(tree));
    }

    private void SearchByTreeCondition(RootNodeViewModel tree)
    {
      var predicate = tree.Compile();
      if (predicate != null)
      {
        SetSearchResults(b => b.Where(predicate));
      }
    }
  }
}